import java.util.ArrayList;
import java.io.*;
import java.net.*;
 
public class Dealer
{
        private ArrayList<Player> players;
        private ArrayList<BufferedReader> readers;
        private ArrayList<PrintWriter> writers;
        private Player[] ghostPlayers;
        private String line = "";
        private int startChips, smallBlind;
        private int numberOfPlayer;
        private Deck deck;
        private Pot pot;
        private int[] auctionTable;
        private int playersAfter1Round, activePlayer, posBigBlind;
        private boolean isFinish;
 
        public Dealer(int start, int small, int num)
        {
                startChips = start;
                smallBlind = small;
                numberOfPlayer = num;
                players = new ArrayList<Player>();
                readers = new ArrayList<BufferedReader>();
                writers = new ArrayList<PrintWriter>();
                ghostPlayers = new Player[num];
                pot = new Pot();
                auctionTable = new int[num];
                deck = new Deck();
                System.out.println("Utworzono talie");
        }
 
        public void saveNrPlayers()
        {
                playersAfter1Round = 0;
                for ( int i=0; i < auctionTable.length; i++)
                {
                        if (auctionTable[i] >= 1) playersAfter1Round = playersAfter1Round + 1;
                }
        }      
 
        public void giveCards()
        {
                for(int i=0; i< players.size(); i++)
                {
                        Card[] temp = deck.getCards(4);
                        players.get(i).setMyHand(temp);
 
                        String msg = "CARDS";
                        for(Card t: temp)
                                msg += " " + t.toString();
                        System.out.println(msg);
                        writers.get(i).println(msg);
                }
        }
 
        public void initializaleAuctionTable()
        {
                for (int x=0; x < auctionTable.length; x++) //petla inicjalizacyjna
                {
                        auctionTable[x]=2; // domyslnie kazdy bierze udzial w licytacji
                }
                System.out.println("Zainicjalizowalem tablice aukcyjna");
        }
       
 
        public void createPlayer(String nickname, PrintWriter writer, BufferedReader reader, boolean human)
        {      
        	if ( human == true)
        	{	
                players.add(new Player(nickname, human, startChips));
                readers.add(reader);
                writers.add(writer);
        	}
        	else
        	{
        		players.add(new Bot(nickname, human, startChips));
                readers.add(reader);
                writers.add(writer);
        	}
        }
       
        private boolean checkPlayer(int position)//sprawdza czy jest gracz na liscie graczy
        {
                if (ghostPlayers[position] == null) // sprawdzic czy jak null w tym elemencie tablicy to czy wywali blad
                        return true;
                return false;
        }
 
        public boolean getIsFinish()
        {
                return isFinish;
        }
 
        public void giveToWinner(int winner)
        {
                if ( winner >= 0) // to gdy nie bylo remisu
                {
                        Player gamer = players.get(winner);
                        if ( auctionTable[winner] > 1) // jezli to nie all-in
                        {
                                gamer.setChips(gamer.getChips()+pot.getAmount());
                                pot.setAmount(0);
                        }
                        else if ( auctionTable[winner] == 1 )
                        {
                                gamer.setChips(gamer.getBetedChips()*playersAfter1Round);
                                playersAfter1Round = 0;
                                pot.setAmount(0);
                        }
                }
                else if ( winner == -1) pot.setAmount(0); // remis
        }
 
        public void startAuction(int posDealerButton, int round)
        {
                System.out.println("poczatek licytacji");
                int counter = 0;
                int dealerButton = posDealerButton;
                numberOfPlayer = players.size();
                posBigBlind = ((dealerButton+2) % numberOfPlayer);
               
                if (round == 0)
                {
                        initializaleAuctionTable();
               
                        autoPay(((dealerButton+1) % numberOfPlayer), smallBlind);
                        auctionTable[(dealerButton+1) % numberOfPlayer]=3;
                       
                        String msg = "SMALLBLIND " + Integer.toString(smallBlind);
                        msg += " " + Integer.toString(players.get((dealerButton+1) % numberOfPlayer).getChips());
                        msg += " " + Integer.toString(players.get((dealerButton+1) % numberOfPlayer).getBetedChips());
                        writers.get((dealerButton+1)  % numberOfPlayer).println(msg);
                        autoPay(((dealerButton+2) % numberOfPlayer), 2*smallBlind);
                        auctionTable[(dealerButton+2) % numberOfPlayer]=3;
                        msg = "BIGBLIND " + Integer.toString(2*smallBlind);
                        msg += " " + Integer.toString(players.get((dealerButton+2) % numberOfPlayer).getChips());
                        msg += " " + Integer.toString(players.get((dealerButton+2) % numberOfPlayer).getBetedChips());
                        writers.get((dealerButton+2) % numberOfPlayer).println(msg);
 
                        writers.get((dealerButton+1)  % numberOfPlayer).println("POT "+Integer.toString(pot.getAmount()));
                        writers.get((dealerButton+2)  % numberOfPlayer).println("POT "+Integer.toString(pot.getAmount()));
                }
                else if (round > 0)
                {
                        for (int x=0; x < auctionTable.length; x++)
                                if ( auctionTable[x] >= 2 )
                                        auctionTable[x] = 3; //wezwij do licytacji wszystkich ktorzy moga licytowac
                }
                       
                while(!isFinish())
                {
                        System.out.println("Jestem w petli z isFinish!");
                        for(int whoPlay=0; whoPlay <= numberOfPlayer; whoPlay++)
                        {
                                activePlayer = (posBigBlind + 1 + whoPlay) % numberOfPlayer; //oblicza pozycje aktualnie licytujacego gracza
                                if (auctionTable[activePlayer] < 2) continue;
                               
                                if(checkPlayer(activePlayer)) //sprawdza czy gracz nie wyszedl podczas swojej tury, jak nie to krupier automatycznie sfloduje
                                {
                                        activeHim(activePlayer);                       
                                }
                                else // przypadek gdy gracz wyszedl podczas licytacji przed swoja aktywacja
                                {
                                        autoMove(activePlayer);
                                }
                        }
                }
                isFinish = isWinner(); 
        }
 
        public boolean isWinner() //okresla czy jest zwyciezca licytacji(), true jesli jest
        {
        		System.out.println("Sprawdzam czy ktos wygral licytacje!");
                int counter = 0;
                boolean finish;
                for (int i=0; i< auctionTable.length; i++)
                {
                        if (auctionTable[i] >= 2 ) counter = counter + 1; // gdy ktos wygral licytacje - 1winner, reszta fold/all-in counter pokaze 1
                }
                if (counter == 1) finish = true;
                        else finish = false;
                return finish;
        }      
 
        private boolean isFinish() //true - jesli licytacja sie zakonczyla, false jesli nie
        {
                int counter = 0;
                for (int i=0; i< auctionTable.length; i++)
                {
                        if (auctionTable[i]== 3) counter = counter + 1;
                }
                System.out.println("Counter wynosi: " + counter);
                if (counter >= 2) return false; // co najmniej jeden gracz moze odpowiedziec na ostatni zaklad
                else return true;
        }
 
        public int whoWin()
        {
                int counter = 0;
                int highestCards = 0;
                for (int x=0; x < numberOfPlayer; x++) /*Wyroznia graczy z najwieksza iloscia kart w ukladzie, reszta zeruje*/
                {
                        if ( auctionTable[x] >=1 )
                        {
                                int number = (players.get(x)).getNumberOfBestCard(); //liczba kart tworzacych uklad danego gracza
                                if ( number > highestCards )
                                {
                                        highestCards = number;
                                        if ( x > 0) // jezli to nie jest pierwszy gracz zignoruj wszystkich do tej pory co mieli mniej kart
                                        {
                                                for (int i=0; i < x; i++)
                                                {
                                                        auctionTable[i] = 0;
                                                }
                                        }
                                }
                                else if ( number < highestCards) auctionTable[x]=0; //nie bďż˝dzie brany pod uwage w dalszym sprawdzaniu
                        }
                }
                /*W tym momencie w auctionTable powyzej 0 sa tylko ci co doszli do konca licytacji i maja
                 * najwiecej kartowy uklad, liczba tego ukladu jest okreslona w highestCard*/
                int[] temp = new int[howMuchPlayers()]; // sluzy do uwzgledniania tylko tych graczy ktorzy zostali
                counter = 0;
                for (int i=0; i < numberOfPlayer; i++)
                {
                        if (auctionTable[i] > 0) // Graczy ktďż˝rzy zostali wpisuje do tymczasowej tablicy z ich indeksami w players
                        {
                                temp[counter] = i;
                                counter = counter + 1;
                        }
                }
                int smallestValue = 14;
                boolean notExit = true;
                boolean isDraw = false;
                int winner = -1; // gdy -1 znajdzie siďż˝ w return tzn ze cos nie tak
                while (notExit)
                {
                        int howCard = 0; //okresla ktďż˝rďż˝ karte z koleji ( jeďż˝li chodzi o wartoďż˝ďż˝ poczynajďż˝c od najniďż˝szej ) pokaze getCard
                        for (int i=0; i < temp.length; i++)
                        {
                                if ( temp[i] < 0 ) continue; // pomijaj graczy ktďż˝rzy juďż˝ zostali odrzuceni
                                int valueOfCard = (players.get(temp[i])).getPlayerCard(howCard);
                                if ( valueOfCard < 1 ) continue; // pomijaj karty ktďż˝re sďż˝ odrzucone
                                if ( smallestValue > valueOfCard )
                                {
                                        smallestValue = valueOfCard;
                                        if ( i > 0) //odrzuc tych co mieli mniejsze karty
                                        {
                                                for (int j=0; j< i; j++)
                                                {
                                                        temp[j]= -1; //-1 oznacza ďż˝e zostali odrzuceni
                                                }
                                        }
                                }
                                else if ( smallestValue < valueOfCard ) temp[i] = -1;
                        }
                        counter = 0;
                        for (int k=0; k < temp.length; k++)
                        {
                                if ( temp[k] >=0 ) counter = counter +1;
                        }
                        if (counter == 1)
                        {
                                notExit = false; // tylko jedna osoba ma ktďż˝rďż˝ďż˝ z koleji ( okreďż˝la howCard ) kartďż˝ jako najniďż˝szďż˝
                                isDraw = false;
                        }
                        else if ( (counter > 1) && (howCard < (highestCards-1)) ) howCard = howCard +1;
                        else if ( (counter >1) && (howCard == (highestCards-1)) )
                        {
                                notExit = false; //remis
                                isDraw = true;
                        }
                }
                if (isDraw) return -1;
                if (!isDraw)
                {
                        for (int i=0; i < temp.length; i++)
                        {
                                if ( temp[i] >= 0) winner = temp[i]; // do winner zapisujemy uchowany indeks z temp, ci co odpadli majďż˝ -1 zamiast indeksu
                        }
                        return winner;
                }
                return winner; // sprawdzic czy zwroci winnera z wartoscia zapisana w if(!isDraw)
        }
       
        private int howMuchPlayers() //zwraca liczbe ile graczy doszlo do konca licytacji
        {
                int counter = 0;
                for (int i=0; i< auctionTable.length; i++)
                {
                        if (auctionTable[i] >= 1 ) counter = counter + 1; //zlicza wszystkich ktorzy nie zfoldowali
                }
                return counter;
        }
 
        public Card[] changeCards(int[] indexes, int position) // w indexes wartosc 0 oznacza karty ktore zostaja
        {
                Card[] newHand = new Card[4];
                Card[] newCards;
                int counter=0;
                for (int j=0; j<indexes.length; j++) // zliczy ktore karty trzeba wymienic
                        if (indexes[j] > 0) counter = counter +1;
               
                newCards = deck.getCards(counter);
                Player player = players.get(position);
                int j = 0;
                for (int i=0; i < indexes.length; i++)
                {
                        if ( indexes[i]>0 )
                        {
                                deck.removeFromHand( player.getCard(i) );
                                newHand[i]=newCards[j];
                                j++;
                        }
                        else if ( indexes[i]==0 )
                        {
                                newHand[i] = player.getCard(i);
                        }
                }
                return newHand;
        }
       
        public void startChanging(int dbPosition)
        {
                for (int i=0; i<auctionTable.length; i++)
                {
                        if (auctionTable[i] < 1) continue;
                        else
                                activeHimChanging(i);                          
                }
        }
       
        private void activeHimChanging(int activePlayer)
        {
                players.get(activePlayer).setActiveStatusChanging(true);
                line = "";
       
                while( players.get(activePlayer).getActiveStatusChanging())
                {
                        // players[activePlayer] trzeba aktywowaďż˝ w tym miejscu przyciski - KOMUNIKACJA SERWER-GUI
                        /*if(aktualnaData-dataNaWejsciu - domyslnie sekundy > 30 )  to zmien status na false i wyjdz
                        *{
                        *       setActiveStatus(false);
                        *       cleanRaiseField(); // wyczyďż˝ci to co zostaďż˝o wpisane w Raise
                        *}
                        */
                        do
                        {
                                try
                                {
                                        line = readers.get(activePlayer).readLine();
                                }
                                catch(IOException e)
                                {
                                }
                        }
                        while(!line.equals("CAN I START"));
                        writers.get(activePlayer).println("YES C");
                        String[] help = {"EMPTY", "EMPTY"};
                        line = "";
                        int count = 0;
                        do
                        {
                                try
                                {
                                        line = readers.get(activePlayer).readLine();
                                        help = line.split(" ");                        
                                }
                                catch(IOException e)
                                {
                                }                      
                        }
                        while(!help[0].equals("CHANGE"));
                        int[] indexes = new int[4];
 
                        try
                        {
                                for(int i = 1; i < help.length; i++)
                                {
                                        indexes[i - 1] = Integer.parseInt(help[i]);
                                        if(indexes[i - 1] == 1)
                                                count++;
                                }
                        }
                        catch(NumberFormatException e)
                        {
                                return;
                        }
                        Card[] newHand = changeCards(indexes, activePlayer);
                        String msg = "CARDS";
                       
                        players.get(activePlayer).setMyHand(newHand);
                        for(Card t: newHand)
                                msg += " " + t.toString();
                        writers.get(activePlayer).println(msg);
                        System.out.println(msg);
                        String answer = players.get(activePlayer).getName() + " CHANGED " + Integer.toString(count);
                        for(int i = 0; i < players.size(); i++)
                                if(i != activePlayer)
                                        writers.get(i).println(answer);
                        players.get(activePlayer).setActiveStatusChanging(false);
                }
        }
 
        public void activeHim(int activePlayer)
        {
                line = "";
                System.out.println("Aktywny gracz nr " + Integer.toString(activePlayer) + " " +  players.get(activePlayer).getName()+":");
                players.get(activePlayer).setActiveStatus(true);
                while(players.get(activePlayer).getActiveStatus())
                {
                        do
                        {
                                try
                                {
                                        line = readers.get(activePlayer).readLine();
                                }
                                catch(IOException e)
                                {
                                }
                        }
                        while(!line.equals("CAN I START"));
                        writers.get(activePlayer).println("YES A");
                        line = "";
                        do
                        {
                                try
                                {
                                        line = readers.get(activePlayer).readLine();
                                }
                                catch(IOException e)
                                {
                                }                      
                        }
                        while(!(line.substring(0, 3).equals("BET") || line.substring(0, 4).equals("FOLD") || line.substring(0, 4).equals("CALL") || line.substring(0, 5).equals("RAISE") || line.substring(0, 5).equals("CHECK") ||line.substring(0, 6).equals("ALL-IN")));
                                       
                        System.out.println(players.get(activePlayer).getName()+"->"+line);
                        if(line.substring(0, 3).equals("BET"))
                        {
                                String[] help = line.split(" ");
                                try
                                {
                                        int betted = Integer.parseInt(help[1]);
                                        if(!bet(activePlayer, betted))
                                        {
                                                writers.get(activePlayer).println("ERROR");
                                                continue;
                                        }
                                        else
                                        {
                                                String msg = "OK " + Integer.toString(players.get(activePlayer).getChips())  + " ";
                                                msg += Integer.toString(players.get(activePlayer).getBetedChips());
                                                writers.get(activePlayer).println(msg);
                                                break;
                                        }
                                }
                                catch(NumberFormatException e)
                                {
                                }
                        }
                        else if(line.substring(0, 4).equals("CALL"))
                        {
                                try
                                {
                                        if(!call(activePlayer))
                                        {
                                                writers.get(activePlayer).println("ERROR");
                                                continue;
                                        }
                                        else
                                        {
                                                String msg = "OK " + Integer.toString(players.get(activePlayer).getChips())  + " ";
                                                msg += Integer.toString(players.get(activePlayer).getBetedChips());
                                                writers.get(activePlayer).println(msg);
                                                break;
                                        }
                                }
                                catch(NumberFormatException e)
                                {
                                }
                        }
                        else if(line.substring(0, 4).equals("FOLD"))
                        {
                                fold(activePlayer);
                                break;
                        }
                        else if(line.substring(0, 5).equals("RAISE"))
                        {
                                String[] help = line.split(" ");
                                try
                                {
                                        int betted = Integer.parseInt(help[1]);
                                        if(!setRaise(activePlayer, betted))
                                        {
                                                writers.get(activePlayer).println("ERROR");
                                                continue;
                                        }
                                        else
                                        {
                                                String msg = "OK " + Integer.toString(players.get(activePlayer).getChips())  + " ";
                                                msg += Integer.toString(players.get(activePlayer).getBetedChips());
                                                writers.get(activePlayer).println(msg);
                                                break;
                                        }
                                }
                                catch(NumberFormatException e)
                                {
                                }
                        }
                        else if(line.substring(0, 5).equals("CHECK"))
                        {
                                if(!check(activePlayer))
                                {
                                        writers.get(activePlayer).println("ERROR");
                                        continue;
                                }
                                else
                                {
                                        String msg = "OK " + Integer.toString(players.get(activePlayer).getChips())  + " ";
                                        msg += Integer.toString(players.get(activePlayer).getBetedChips());
                                        writers.get(activePlayer).println(msg);
                                        break;
                                }
                        }
                        else if(line.substring(0, 6).equals("ALL-IN"))
                        {
                                if(!allIn(activePlayer))
                                {
                                        writers.get(activePlayer).println("ERROR");
                                        continue;
                                }
                                else
                                {
                                        String msg = "OK " + Integer.toString(players.get(activePlayer).getChips())  + " ";
                                        msg += Integer.toString(players.get(activePlayer).getBetedChips());
                                        writers.get(activePlayer).println(msg);
                                        break;
                                }
                        }
                               
                }
 
                String answer = players.get(activePlayer).getName()+ " " + line;
                for(int i = 0; i < players.size(); i++)
                        if(i != activePlayer)
                                writers.get(i).println(answer);
 
                for(int i = 0; i < players.size(); i++)
                        writers.get(i).println("POT "+Integer.toString(pot.getAmount()));
        }
 
        private void autoPay(int position, int blind)
        {
                Player gamer = players.get(position);
                if (gamer.getChips() < blind) allIn(position); //automatyczne All-In gdy brak kasy na ciemna
                else
                {
                        gamer.setChips(gamer.getChips()-blind);
                        pot.addToPot(blind);
                        auctionTable[position]=2;
                        gamer.setBetedChips(blind);
                        pot.setHighestBet(blind);
                        gamer.setActiveStatus(false);
                }
        }      
 
        private void autoMove(int position)
        {
                fold(position);
        }
 
        private boolean bet(int position, int bet) //gracz daje bet100 ale zaplacil mala ciemne 25 wiec zaplaci 75 ( inaczej niz w raise )
    {
        /*Na wejsciu jesli to poczatek gry BettedChips musi byc =0 lub SB / BB jesli ktos juz zaplacil automatycznie ciemne */
        Player gamer = players.get(position);
        int summaryCost = bet - gamer.getBetedChips(); //calkowity koszt - moze betowac jak zaplacil BigBlind i to co stawia to roznica bet-BB
        if(bet > pot.getHighestBet() && (pot.getHighestBet() == 2*smallBlind)) // nie trzeba sprawdzac czy stac go, to uzywane jest przy pierwszej rundzie
        {
               gamer.setChips(gamer.getChips()-summaryCost);
               pot.addToPot(summaryCost);
               for(int i=0; i <= numberOfPlayer; i++)
               {
                      if (auctionTable[i] == 2) auctionTable[i] =3; //aktywuj graczy ktorzy sprawdzili poprzednie przebicie
               }
               pot.setHighestBet(bet);
               gamer.setBetedChips(gamer.getBetedChips() + summaryCost);
               gamer.setActiveStatus(false);
               return true;
        }
                return false;
   }
   
	private boolean setRaise(int position, int playerBet)
    {
        Player gamer = (players.get(position));
 		int summaryCost = playerBet + (pot.getHighestBet() - gamer.getBetedChips());
     	if (summaryCost < gamer.getChips())
        {              
              gamer.setChips( gamer.getChips()- summaryCost );
              pot.addToPot( summaryCost );
              gamer.setBetedChips( gamer.getBetedChips() + summaryCost );                            
                  pot.setHighestBet( pot.getHighestBet() + playerBet );
              for(int i=0; i < numberOfPlayer; i++)
              {
                    if (auctionTable[i] == 2) auctionTable[i] =3; //aktywuj graczy ktorzy sprawdzili poprzednie przebicie
              }
              gamer.setActiveStatus(false);
              return true;
        }
        return false;//wyswietl komunikat w GUI ze postawil wiecej niz posiada        
    }
       
        private boolean check(int position)
        {
                if(players.get(position).getBetedChips() == pot.getHighestBet() )
                {
                        players.get(position).setActiveStatus(false);
                        /*auctionTable[position] = 2; jesli masz najwieksze przebicie to czekaj, 
                         *check mozliwy gdy nikt nie przebil wczesniej , czyli tylko obecny gracz ma 3*/
                        return true;
                }
                return false;
        }
 
        private boolean call(int position)
        {
                Player gamer = players.get(position);
                int alignment = pot.getHighestBet() - gamer.getBetedChips();
                if (alignment < gamer.getChips() )
                {
                        gamer.setChips( gamer.getChips() - alignment );
                        pot.addToPot(alignment);
                        auctionTable[position] = 2;
                        gamer.setBetedChips( pot.getHighestBet() );
                        gamer.setActiveStatus(false);
                        return true;
                }
                else
                        return false;//wyswietl w GUI ze ma za malo kasy lub nie zmieniaj statusu i dalej bedzie aktywny */
        }
 
        private void fold(int position)
        {
                (players.get(position)).setActiveStatus(false);
                auctionTable[position] = 0;
        }
        private boolean allIn(int position)
        {
                Player gamer = players.get(position);
                int chips = gamer.getChips();
                if( ( chips < ( pot.getHighestBet()-gamer.getBetedChips()) ) && (chips>0) )
                {
                        pot.addToPot(chips);
                        gamer.setBetedChips(gamer.getBetedChips() + chips);
                        gamer.setChips(0);
                        auctionTable[position] = 1;    
                        gamer.setActiveStatus(false);
                        return true;
                }
                return false;
        }
        public void reset()
        {
        	pot.setAmount(0);
        	pot.setHighestBet(0);
        	for (int i=0; i< players.size(); i++)
        	{
        		Player gamer = players.get(i);
        		gamer.setChips(startChips);
        		gamer.setBetedChips(0);
        		gamer.setActiveStatus(false);
        		gamer.setActiveStatusChanging(false);
        		/*update okien*/
        		/*zabierz od graczy karty i wtasuj je do tali, tak samo te z kupki wymienionych*/
        	}
        }
        public String getPlayerName(int who)
    	{
    		return (players.get(who)).getName();
    	}
}
